package com.ys.utils.yinsheng.yztdock.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: zf
 * @Description: 银账通接口对接类
 * @ClassName: YztDockUtil
 * @Date: 2020/6/23 11:29
 * @Version:
 */
public class YztDockUtil {

    private static final String CHAR_SET = "UTF-8";

    private static final String TEST = "test";

    private static final String PROD = "prod";

    /** test */
    private static final String PUBLIC_URL_TEST = "http://bd4-vtest.ysepay.com/api/";

    /** prod */
    private static final String PUBLIC_URL_PROD = "https://yzt.ysepay.com:8443/api/";

    private static final Map<String, Object> certMap = new ConcurrentHashMap<String, Object>();

    private static final Map<String, Object> privateMap = new ConcurrentHashMap<String, Object>();

    private static Logger logger = LoggerFactory.getLogger(YztDockUtil.class);


    /**
     * @Author: zf
     * @Description: 请求银盛接口
     * @Date: 2020/6/23 19:18
     * @param reqData       请求参数
     * @param apiName       请求接口名称
     * @return: java.util.Map<java.lang.String,java.lang.String>
     */
    public static Map<String,String> sendRequest(String reqData,String apiName){

        logger.info("请求入参{reqData:" + reqData + ",apiName:" + apiName + "}");

        Map<String, String> paramsMap = PropertiesUtil.getParamsMap();

        // 校验参数
        paramCheck(reqData,apiName);

        CertificateFactory cf = null;
        FileInputStream in = null;
        FileInputStream fis = null;
        Certificate certificate = null;
        KeyStore ks = null;
        PublicKey publicKey = null;
        PrivateKey privateKey = null;
        String result = null;

        try {
            // 获取公钥证书
            cf = CertificateFactory.getInstance("X.509");
            in = new FileInputStream(paramsMap.get("ys.crt.path"));
            certificate = cf.generateCertificate(in);
            publicKey = certificate.getPublicKey();

            logger.info("公钥证书加载完毕！");

            // 获取私钥证书
            ks = KeyStore.getInstance("PKCS12");
            fis = new FileInputStream(paramsMap.get("mer.pfx.path"));
            char[] nPassword = paramsMap.get("mer.pfx.pwd").toCharArray();
            ks.load(fis, nPassword);

            Enumeration<String> aliasenum = ks.aliases();
            String keyAlias = null;
            if (aliasenum.hasMoreElements()) {
                keyAlias = aliasenum.nextElement();
            }
            privateKey = (PrivateKey) ks.getKey(keyAlias, nPassword);

            logger.info("私钥证书加载完毕！");
        } catch (Exception e) {
            logger.info("公私钥证书获取失败！");
            throw new RuntimeException("公私钥证书获取异常:"+e);
        }

        // 获取AES key
        String key = SignUtil.getRandomKey();
        // AES加密请求参数
        String msg = SignUtil.encryptMessage(reqData, key);
        // 加密AES key
        String encryptKey = SignUtil.encryptKey(key,publicKey);

        logger.info("加密的AES秘钥:[ " + encryptKey + " ]");

        // 商户私钥加密请求参数
        Map<String, String> requestParameter = SignUtil.getRequestParameter("02", apiName, msg, encryptKey,paramsMap.get("mer.cert.id"),privateKey);

        logger.info("请求银盛接口"+apiName+"参数:[ " + requestParameter + " ]");


        String requestUrl = "";

        String active = paramsMap.get("active");

        if(TEST.equals(active)){
            requestUrl = PUBLIC_URL_TEST + apiName;
        }else if(PROD.equals(active)){
            requestUrl = PUBLIC_URL_PROD + apiName;
        } else {
            throw new RuntimeException("yzt.properties未设置active 或者 active 设置不正确,请查看README.txt并重新配置");
        }

        logger.info("请求银盛URL: [ " + requestUrl + " ]");

        // 发送Http Post请求
        HttpClient hc = new HttpClient(requestUrl, 30000, 30000);// 连接超时时间，读超时时间（可自行判断，修改）
        String resultString = null;

        try {
            int status = hc.send(requestParameter, CHAR_SET);
            if (200 == status) {
                resultString = hc.getResult();
            } else {
                logger.info("返回http状态码[" + status + "]，请检查请求报文或者请求地址是否正确");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        logger.info("请求返回结果: [ " + resultString + " ]");

        // 解密验签请求结果
        return decryptRspMsg(resultString, key, publicKey);
    }

    /**
     * @Author: zf
     * @Description: 发银盛请求前加密请求参数
     * @Date: 2020/6/23 19:18
     * @param reqData       请求参数
     * @param apiName       请求接口名称
     * @return: boolean
     */
    private static void paramCheck(String reqData, String apiName) {
        if(StringUtils.isEmpty(reqData)){
            throw new RuntimeException("请求参数 --reqData-- 不能为空");
        }

        if(StringUtils.isEmpty(apiName)){
            throw new RuntimeException("请求参数 --apiName-- 不能为空");
        }
    }

    public static Map<String, String> sendRequest(String reqData, String apiName, File file) {
        logger.info("请求入参{reqData:" + reqData + ",apiName:" + apiName + "}");
        Map<String, String> paramsMap = PropertiesUtil.getParamsMap();
        paramCheck(reqData, apiName);

        PublicKey publicKey = null;
        PrivateKey privateKey = null;


        String encryptKey;
        try {
            publicKey = getPublicKeyFromCert();
            privateKey = getPrivateKeyFromPKCS12();
        } catch (Exception var22) {
            logger.info("公私钥证书获取失败！");
            throw new RuntimeException("公私钥证书获取异常:" + var22);
        }

        String key = SignUtil.getRandomKey();
        String msg = SignUtil.encryptMessage(reqData, key);
        encryptKey = SignUtil.encryptKey(key, publicKey);
        logger.info("加密的AES秘钥:[ " + encryptKey + " ]");
        Map<String, String> requestParameter = SignUtil.getRequestParameter("02", apiName, msg, encryptKey, (String)paramsMap.get("mer.cert.id"), privateKey);
        logger.info("请求银盛接口" + apiName + "参数:[ " + requestParameter + " ]");
        String requestUrl = "";
        String active = (String)paramsMap.get("active");
        if ("test".equals(active)) {
            requestUrl = "http://bd4-vtest.ysepay.com/api/" + apiName;
        } else {
            if (!"prod".equals(active)) {
                throw new RuntimeException("yzt.properties未设置active 或者 active 设置不正确,请查看README.txt并重新配置");
            }

            requestUrl = "https://yzt.ysepay.com:8443/api/" + apiName;
        }

        logger.info("请求银盛URL: [ " + requestUrl + " ]");
        if (apiName.contains("uploadPicture")) {
            if (null == file) {
                throw new RuntimeException("图片上传文件不能为空！");
            } else {
                Map<String, String> reqMap = (Map) JSON.parseObject(reqData, Map.class);
                return sendHttpMessage(reqMap, requestUrl, "UTF-8", file);
            }
        } else {
            HttpClient hc = new HttpClient(requestUrl, 30000, 30000);
            String resultString = null;

            try {
                int status = hc.send(requestParameter, "UTF-8");
                if (200 == status) {
                    resultString = hc.getResult();
                } else {
                    logger.info("返回http状态码[" + status + "]，请检查请求报文或者请求地址是否正确");
                }
            } catch (Exception var21) {
                var21.printStackTrace();
            }

            logger.info("请求返回结果: [ " + resultString + " ]");
            return decryptRspMsg(resultString, key, publicKey);
        }
    }

    /**
     * 读取公钥，x509格式
     *
     * @return
     * @throws Exception
     * @see
     */
    private static PublicKey getPublicKeyFromCert() throws Exception {
        PublicKey pubKey = (PublicKey) certMap.get("PublicKey");
        if (pubKey != null) {
            return pubKey;
        }
        InputStream  ins = null;
        try {
            Map<String, String> paramsMap = PropertiesUtil.getParamsMap();
            ins = new FileInputStream(paramsMap.get("ys.crt.path"));
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            Certificate cac = cf.generateCertificate(ins);
            pubKey = cac.getPublicKey();
            certMap.put("PublicKey", pubKey);
            logger.info("公钥证书加载完毕！");
        } catch (Exception e) {
            if (ins != null)
                ins.close();
            throw e;
        } finally {
            if (ins != null) {
                ins.close();
            }
        }
        return pubKey;
    }

    /**
     * 读取PKCS12格式的key（私钥）pfx格式
     *
     * @return
     * @throws Exception
     * @see
     */
    public static PrivateKey getPrivateKeyFromPKCS12() throws Exception {
        PrivateKey priKey = (PrivateKey) privateMap.get("PrivateKey");
        if (priKey != null) {
            return priKey;
        }
        KeyStore keystoreCA = KeyStore.getInstance("PKCS12");
        InputStream fis = null;
        try {
            Map<String, String> paramsMap = PropertiesUtil.getParamsMap();
            fis = new FileInputStream(paramsMap.get("mer.pfx.path"));
            char[] nPassword = (paramsMap.get("mer.pfx.pwd")).toCharArray();
            // 读取CA根证书
            keystoreCA.load(fis, nPassword);

            Enumeration<?> aliases = keystoreCA.aliases();
            String keyAlias = null;
            if (aliases != null) {
                while (aliases.hasMoreElements()) {
                    keyAlias = (String) aliases.nextElement();
                    // 获取CA私钥
                    priKey = (PrivateKey) (keystoreCA.getKey(keyAlias, nPassword));
                    if (priKey != null) {
                        privateMap.put("PrivateKey", priKey);
                        logger.info("私钥证书加载完毕！");
                        break;
                    }
                }
            }
        } catch (Exception e) {
            if (fis != null)
                fis.close();
            throw e;
        } finally {
            if (fis != null) {
                fis.close();
            }
        }

        return priKey;
    }


    /**
     * @Author: zf
     * @Description: 验签、解密接口返回结果
     * @Date: 2020/6/24 13:55
     * @param rspStr 调用银盛接口返回的结果
     * @param check  AES秘钥
     * @param publicKey  银盛公钥
     * @return: java.util.Map<java.lang.String,java.lang.String>
     */
    private static Map<String, String> decryptRspMsg(String rspStr,String check,PublicKey publicKey){

        Map<String, String> rspData = new HashMap<String, String>();

        if(rspStr == null || check == null ){
            throw new RuntimeException("解密请求参数不能为空！");
        }

        // 将返回结果转换为JSONObject
        JSONObject jsonObject = JSONObject.parseObject(rspStr);
        // 签名验签
        Set<String> keyIter = jsonObject.keySet();
        String value;
        Map<String, String> valueMap = new HashMap<String, String>();

        for (String str : keyIter) {
            value = jsonObject.get(str).toString();
            valueMap.put(str, value);
        }

        if (valueMap.containsKey("code")) {
            if (!(valueMap.get("code").equals("200")||valueMap.get("code").equals("324"))) {
                rspData.put("code", valueMap.get("code"));
                rspData.put("msg", valueMap.get("msg"));
                return rspData;
            }
        }

        rspData.put("code", "200");
        rspData.put("msg", valueMap.get("msg"));

        try {
            String data = SignUtil.decryptMessage(valueMap,check,publicKey);
            if (!StringUtils.isEmpty(data)) {
                rspData.put("data", data);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        logger.info("接口返回最终结果=========================>"+ JSONObject.toJSONString(rspData));

        return rspData;
    }

    public static Map<String, String> sendHttpMessage(Map<String, String> reqData, String reqUrl, String encoding, File file) {
        Map<String, String> rspData = new HashMap();
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;

        try {
            HttpClientBuilder builder = HttpClients.custom();
            builder.setUserAgent("Mozilla/5.0(Windows;U;Windows NT 5.1;en-US;rv:0.9.4)");
            HttpPost post = new HttpPost(reqUrl);
            MultipartEntity entity = new MultipartEntity();
            Iterator var10 = reqData.entrySet().iterator();

            while(var10.hasNext()) {
                Map.Entry<String, String> entry = (Map.Entry)var10.next();
                entity.addPart((String)entry.getKey(), new StringBody((String)entry.getValue(), Charset.forName("UTF-8")));
            }

            entity.addPart("file", new FileBody(file));
            post.setEntity(entity);
            client = builder.build();
            response = client.execute(post);
            if (response.getStatusLine().getStatusCode() != 200) {
                throw new Exception("请求失败！");
            }

            HttpEntity resEntity = response.getEntity();
            String resultString = null == resEntity ? "" : EntityUtils.toString(resEntity, encoding);
            resultString = new String(RSAUtil.base64Decode(org.apache.commons.codec.binary.StringUtils.getBytesUtf8(resultString)));
            System.out.println("resultString=============>" + resultString);
            if (null != resultString && !"".equals(resultString)) {
                JSONObject jsonObject = JSONObject.parseObject(resultString);
                Set<String> keyIter = jsonObject.keySet();
                Map<String, String> valueMap = new HashMap();
                Iterator var16 = keyIter.iterator();

                while(var16.hasNext()) {
                    String str = (String)var16.next();
                    String value = jsonObject.get(str).toString();
                    valueMap.put(str, value);
                }

                if (valueMap.containsKey("code") && !((String)valueMap.get("code")).equals("200")) {
                    rspData.put("code", valueMap.get("code"));
                    rspData.put("msg", valueMap.get("msg"));
                    return rspData;
                }

                String picId = null;
                rspData.put("code", String.valueOf(200));
                rspData.put("msg", valueMap.get("msg"));

                try {
                    if (!StringUtils.isEmpty((String)valueMap.get("data"))) {
                        picId = new String(RSAUtil.base64Decode(org.apache.commons.codec.binary.StringUtils.getBytesUtf8((String)valueMap.get("data"))));
                    }

                    if (!StringUtils.isEmpty(picId)) {
                        rspData.put("data", picId);
                    }
                } catch (Exception var18) {
                    var18.printStackTrace();
                }
            }
        } catch (Exception var19) {
            LogUtil.writeErrorLog(var19.getMessage(), var19);
        }

        return rspData;
    }
}
